package com.easylinkin.linkappapi.elasticsearch.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.analyze.mapper.AnalyzeDateSourceMapper;
import com.easylinkin.linkappapi.analyze.service.AnalyzeDateSourceService;
import com.easylinkin.linkappapi.common.utils.DateUtil;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.service.DeviceService;
import com.easylinkin.linkappapi.elasticsearch.entity.DeviceEnergyReal;
import com.easylinkin.linkappapi.elasticsearch.entity.DeviceEnergyRealItem;
import com.easylinkin.linkappapi.elasticsearch.entity.ESconfig;
import com.easylinkin.linkappapi.elasticsearch.entity.EsQuerymodel;
import com.easylinkin.linkappapi.elasticsearch.mapper.ElasticsearchRealMapper;
import com.easylinkin.linkappapi.space.service.LinkappAreaService;
import com.easylinkin.linkappapi.tenant.sevice.LinkappTenantService;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

/**
 * Es能源相关的实际操作类
 */
@Service
public class EsEnergyServiceImpl extends ServiceImpl<ElasticsearchRealMapper, DeviceEnergyReal> {

    private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(EsEnergyServiceImpl.class);
    private ESconfig eSconfig;

    @Autowired
    public void setESconfig(ESconfig eSconfig) {
        this.eSconfig = eSconfig;
    }

    @Autowired
    EsEnergyServiceImpl esEnergyServiceImpl;
    @Autowired
    DeviceService deviceService;
    @Autowired
    LinkappAreaService linkappAreaService;
    @Autowired
    LinkappTenantService linkappTenantService;
    @Autowired
    AnalyzeDateSourceService analyzeDateSourceService;
    @Autowired
    AnalyzeDateSourceMapper analyzeDateSourceMapper;
    @Autowired
    EsServiceImpl esServiceImpl;

    /**
     * 查询条件如下： { "query": { "bool": { "must": [ { "terms": { "dataSourceId": [ "2a4fe6d56311a8780b0c52422bdccc5d" ], "boost": 1.0 } }, { "wildcard": { "calcTime": { "wildcard": "2021011813", "boost": 1.0 } } } ], "adjust_pure_negative": true, "boost": 1.0 } } }
     */
    public SearchResponse buildDayEnergyEachDeviceSearchResponse(EsQuerymodel esQuerymodel) {

        Assert.notNull(esQuerymodel.getCurrentBeginTime(), "currentBeginTime 参数为空");
        Assert.notNull(esQuerymodel.getCurrentEndTime(), "currentEndTime 参数为空");
        SearchResponse searchResponse = null;
        String projectId = esServiceImpl.getProjectId();
        RestHighLevelClient hclient = eSconfig.getFactory().getRhlClient();
//        当前小时
        TermQueryBuilder nowHour = QueryBuilders.termQuery("calcTime", new Long(DateUtil.getYYYYMMDDHHNumberString(new Date())));
//        当前小时 前一个小时
        TermQueryBuilder nowHourBefore1h = QueryBuilders.termQuery("calcTime", new Long(DateUtil.getYYYYMMDDHHNumberString(new Date(System.currentTimeMillis() - 60 * 60 * 1000))));
//        查询起始时间
        TermQueryBuilder beginTime = QueryBuilders.termQuery("calcTime", new Long(DateUtil.getYYYYMMDDHHNumberString(esQuerymodel.getCurrentBeginTime())));
//        查询起始时间 的前1h
        TermQueryBuilder beginTimeBefore1h = QueryBuilders.termQuery("calcTime", new Long(DateUtil.getYYYYMMDDHHNumberString(new Date(esQuerymodel.getCurrentBeginTime().getTime() - 60 * 60 * 1000))));
//        查询结束时间
        TermQueryBuilder endTime = QueryBuilders.termQuery("calcTime", new Long(DateUtil.getYYYYMMDDHHNumberString(esQuerymodel.getCurrentEndTime())));
//        查询结束时间 的前1h
        TermQueryBuilder endTimeBefore1h = QueryBuilders.termQuery("calcTime", new Long(DateUtil.getYYYYMMDDHHNumberString(new Date(esQuerymodel.getCurrentEndTime().getTime() - 60 * 60 * 1000))));

        SearchRequest searchRequest = new SearchRequest("aep_etl_" + projectId).source(new SearchSourceBuilder().query(QueryBuilders.boolQuery().must(QueryBuilders.termsQuery("dataSourceId", esQuerymodel.getDataSourceId()))
            .should(nowHour).should(nowHourBefore1h).should(beginTime).should(beginTimeBefore1h).should(endTime).should(endTimeBefore1h).minimumShouldMatch(1)));

        LOGGER.info("查询es条件searchRequest：" + searchRequest.toString());
        try {
            searchResponse = hclient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (Exception e) {
            LOGGER.error("ES查询Table列表数据异常" , e);
        }
        return searchResponse;
    }

    // 按照设备每一天聚合，只有实时查询才会用到，实时数据必须从原始es数据中解析
    public SearchResponse buildDayEnergyEachDeviceSearchResponse(String projectId, EsQuerymodel queryModel) {
//    参数中放置对应的计算配置所配的 属性identifier
        esServiceImpl.putQueryModelAttributeIdentifier(queryModel);
        SearchResponse searchResponse = null;
        RestHighLevelClient hclient = eSconfig.getFactory().getRhlClient();
        List<String> deviceCodeList = new ArrayList<>();
        if (null != queryModel.getDataSourceId()) {
            deviceCodeList = esServiceImpl.getDeviceCodes(queryModel.getDataSourceId());
        }
        if (ObjectUtils.isEmpty(deviceCodeList)) {
            LOGGER.info("根据数据源查得，匹配的设备集合为空，故返回es查询结果 null");
            return searchResponse;
        }
//        由于 2021030400 代表的是 3号的数据
        Date queryTimeEnd = queryModel.getCurrentEndTime();
        queryTimeEnd.setTime(queryTimeEnd.getTime() + 24 * 60 * 60 * 1000);
        Date queryTimeStart = queryModel.getCurrentBeginTime();
        queryTimeStart.setTime(queryTimeStart.getTime());

        QueryBuilder rangeQuery = QueryBuilders.rangeQuery("calcTime")
            .lt(new Long(DateUtil.getYYYYMMDDHHNumberString(queryTimeEnd)))
            .gte(new Long(DateUtil.getYYYYMMDDHHNumberString(queryTimeStart)));
        SearchRequest searchRequest = new SearchRequest("aep_etl_" + projectId);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        QueryBuilder termsQuery = QueryBuilders.termsQuery("items.deviceCode.keyword", deviceCodeList);
        QueryBuilder boolquery = QueryBuilders.boolQuery().must(rangeQuery).must(termsQuery).must(QueryBuilders.termQuery("dataSourceId", queryModel.getDataSourceId()));
        searchSourceBuilder.query(boolquery);
        searchSourceBuilder.size(10000);

        searchRequest.source(searchSourceBuilder);
        LOGGER.info("es 查询,buildDayEnergyEachDeviceSearchResponse方法,条件是：" + searchRequest.toString());
        try {
            searchResponse = hclient.search(searchRequest, RequestOptions.DEFAULT);
            LOGGER.info("ES查询设备列表数据，参数：" + JSON.toJSONString(searchRequest));
        } catch (Exception e) {
            LOGGER.error("ES查询Table列表数据异常" , e);
            log.error("es request  has no  response!");
        }
        return searchResponse;
    }

    public DeviceEnergyReal resolvingDayEnergyEachDeviceList(SearchResponse searchResponse, EsQuerymodel esQuerymodel) {
        if (searchResponse == null) {
            return null;
        }
        SearchHit[] hits = searchResponse.getHits().getHits();
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(Include.NON_EMPTY);
        if (hits.length == 0) {
            return null;
        }
        HashMap<String, DeviceEnergyReal> deviceEnergyRealMap = new HashMap<>(2);
        for (SearchHit hit : hits) {
            DeviceEnergyReal der = JSONObject.parseObject(hit.getSourceAsString(), DeviceEnergyReal.class);
            deviceEnergyRealMap.put(der.getCalcTime(), der);
        }
        String now = DateUtil.getYYYYMMDDHHNumberString(new Date());
//        如果当前小时的值 取不到 就取上一个小时的
        DeviceEnergyReal target = deviceEnergyRealMap.get(now) != null ? deviceEnergyRealMap.get(now) : deviceEnergyRealMap.get(DateUtil.getYYYYMMDDHHNumberString(new Date(System.currentTimeMillis() - 60 * 60 * 1000)));

        String beginTime = DateUtil.getYYYYMMDDHHNumberString(esQuerymodel.getCurrentBeginTime());
        String endTime = DateUtil.getYYYYMMDDHHNumberString(esQuerymodel.getCurrentEndTime());
        DeviceEnergyReal queryStartDeviceEnergyReal = deviceEnergyRealMap.get(beginTime);
        if (queryStartDeviceEnergyReal == null) {
            String beginTimeBefore1h = DateUtil.getYYYYMMDDHHNumberString(new Date(esQuerymodel.getCurrentBeginTime().getTime() - 60 * 60 * 1000));
            queryStartDeviceEnergyReal = deviceEnergyRealMap.get(beginTimeBefore1h);
        }
        DeviceEnergyReal queryEndDeviceEnergyReal = deviceEnergyRealMap.get(endTime);
        if (queryEndDeviceEnergyReal == null) {
            String endTimeBefore1h = DateUtil.getYYYYMMDDHHNumberString(new Date(esQuerymodel.getCurrentEndTime().getTime() - 60 * 60 * 1000));
            queryEndDeviceEnergyReal = deviceEnergyRealMap.get(endTimeBefore1h);
        }
        if (queryStartDeviceEnergyReal == null || queryEndDeviceEnergyReal == null) {
            return target;
        }
        if (target == null || ObjectUtils.isEmpty(target.getItems())) {
            return target;
        }
        for (DeviceEnergyRealItem item : target.getItems()) {
            for (DeviceEnergyRealItem queryStartItem : queryStartDeviceEnergyReal.getItems()) {
                for (DeviceEnergyRealItem queryEndItem : queryEndDeviceEnergyReal.getItems()) {
                    if (item.getDeviceCode().equals(queryStartItem.getDeviceCode()) && item.getDeviceCode().equals(queryEndItem.getDeviceCode())) {
                        item.setPeriodStatisticsValue(new BigDecimal(queryEndItem.getTotal()).subtract(BigDecimal.valueOf(queryStartItem.getTotal())).setScale(4, BigDecimal.ROUND_HALF_UP).doubleValue());
                    }
                }
            }
        }

        return target;
    }

    public Map<String, List<DeviceEnergyReal>> resolveHistoricEnergyEachDeviceList(SearchResponse searchResponse, EsQuerymodel queryModel) throws JsonProcessingException {
//        结果集 key:每日日期字符串, value:设备数据列表
        Map<String, List<DeviceEnergyReal>> deviceEnergyRealListMap = new LinkedHashMap<>();
        List<DeviceEnergyReal> deviceEnergyRealList = new ArrayList<>();
        if (searchResponse == null) {
            return null;
        }
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);

//        查询结果中的 设备编号集合
        Set<String> deviceCodesSet = new TreeSet<>();

        SearchHits searchHits = searchResponse.getHits();
        SearchHit[] hit = searchHits.getHits();
        for (SearchHit documentFields : hit) {
            Map<String, Object> source = documentFields.getSourceAsMap();
            ArrayList<HashMap<String, Object>> items = (ArrayList<HashMap<String, Object>>) source.get("items");
            String calcTimeStr = source.get("calcTime").toString();
            if (!calcTimeStr.endsWith("00")) {
                continue;
            }
            for (HashMap item : items) {
                DeviceEnergyReal deviceEnergyReal = JSON.parseObject(JSON.toJSONString(item), DeviceEnergyReal.class);
                deviceEnergyReal.setCalcTime(calcTimeStr);
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHH");
                try {
                    Date date = simpleDateFormat.parse(deviceEnergyReal.getCalcTime());
//                    2021012700 代表的是 26 号的一天的数据
                    date = new Date(date.getTime() - 24 * 60 * 60 * 1000);
                    deviceEnergyReal.setCreateTime(date);
                } catch (ParseException e) {
                    log.error("resolveHistoricEnergyEachDeviceList  error", e);
                    e.printStackTrace();
                }
                deviceEnergyReal.setDayEnergy(deviceEnergyReal.getDay());
                deviceEnergyReal.setSumEnergy(deviceEnergyReal.getTotal());
                deviceEnergyRealList.add(deviceEnergyReal);
                deviceCodesSet.add(deviceEnergyReal.getDeviceCode());
            }
        }
        List<Device> devices = deviceService.selectDevicesByCodes(deviceCodesSet, new Device());
        Map<String, Device> deviceMap = new HashMap<>();
        for (Device device : devices) {
            deviceMap.put(device.getCode(), device);
        }

        for (DeviceEnergyReal deviceEnergyReal : deviceEnergyRealList) {
            Device device = deviceMap.get(deviceEnergyReal.getDeviceCode());
            deviceEnergyReal.setDeviceName(device.getName());
            deviceEnergyReal.setUniqueMark(device.getDeviceUnitCode());
            deviceEnergyReal.setDeviceUnitName(device.getDeviceUnitName());
        }
        for (String deviceCode : deviceCodesSet) {
            List<DeviceEnergyReal> deviceEnergyRealEachList = new ArrayList<>();
            for (DeviceEnergyReal deviceEnergyReal : deviceEnergyRealList) {
                if (deviceCode.equals(deviceEnergyReal.getDeviceCode())) {
                    deviceEnergyRealEachList.add(deviceEnergyReal);
                }
            }
            deviceEnergyRealEachList = deviceEnergyRealEachList.stream().sorted(Comparator.comparing(DeviceEnergyReal::getCreateTime).reversed()).collect(Collectors.toList());

            deviceEnergyRealListMap.put(deviceCode, deviceEnergyRealEachList);
        }

        return deviceEnergyRealListMap;
    }


}
